package com.system.demo.commonService;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.system.demo.entity.comment.RankingItem;
import com.system.demo.tool.ResponseResult;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.StaticScriptSource;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class RedisService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    private final ObjectMapper objectMapper = new ObjectMapper();

    public void setTokenKey(String token, String key, long expirationInSeconds, TimeUnit timeUnit) {
        // 将对象序列化为 JSON 字符串
        try {
            // 将对象序列化为 JSON 字符串
            redisTemplate.opsForValue().set(token, key, expirationInSeconds, timeUnit);
        } catch (Exception e) {
            throw new RuntimeException("Redis数据序列化失败", e);
        }

    }

    public String getKeyByToken(String token) {
        return redisTemplate.opsForValue().get(token);
    }

    public void setTokenKey(String token, List<?> values, long timeout, TimeUnit timeUnit) {
        // 删除旧的键值对，以确保我们是覆盖而不是追加
        redisTemplate.delete(token);

        // 检查值列表是否为空，如果不为空则设置新的列表
        if (values != null && !values.isEmpty()) {
            try {
                String jsonValue = objectMapper.writeValueAsString(values);
                if (timeout == -1) {
                    // 如果timeout为null或-1，则不设置过期时间

                    redisTemplate.opsForValue().set(token, jsonValue);
                } else {
                    // 否则，设置带有过期时间的键值对
                    redisTemplate.opsForValue().set(token, jsonValue, timeout, timeUnit);
                }
            } catch (Exception e) {
                throw new RuntimeException("Redis数据序列化失败", e);
            }
        }
    }
    public <T> List<T> getTokenKeyAsList(String token, Class<T> clazz) {
        String jsonString = redisTemplate.opsForValue().get(token);
        if (jsonString == null) {
            return null;
        }

        try {
            // 反序列化 JSON 字符串为对象列表
            JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, clazz);
            return objectMapper.readValue(jsonString, javaType);
        } catch (IOException e) {
            throw new RuntimeException("Redis数据反序列化失败", e);
        }
    }

    public List<String> getTokenKey(String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    public void deleteTokenKey(String token) {
        redisTemplate.delete(token);
    }

    /**
     * 尝试获取锁
     *
     * @param key        锁的 Key
     * @param value      锁的唯一值（用于解锁校验）
     * @param expireTime 锁的过期时间（秒）
     * @return 是否成功获取锁
     */
    public boolean tryLock(String key, String value, long expireTime) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofSeconds(expireTime)));
    }

    /**
     * 释放锁
     *
     * @param key   锁的 Key
     * @param value 锁的唯一值（用于校验）
     */
    public void releaseLock(String key, String value) {
        String currentValue = redisTemplate.opsForValue().get(key);
        if (value != null && value.equals(currentValue)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 执行带分布式锁的操作
     *
     * @param lockKey       锁的唯一 Key
     * @param lockExpire    锁的过期时间（秒）
     * @param action        执行的业务逻辑
     * @param <T>           返回结果类型
     * @return 执行结果
     */
    public <T> ResponseResult<T> executeWithLock(String lockKey, long lockExpire, LockAction<T> action) {
        String lockValue = UUID.randomUUID().toString();
        boolean isLocked = tryLock(lockKey, lockValue, lockExpire);

        if (!isLocked) {
            // 锁获取失败，返回友好提示
            return ResponseResult.BadRequest("操作过于频繁，请稍后再试！");
        }

        try {
            // 执行业务逻辑
            T result = action.execute();

            // 如果业务逻辑返回的是 ResponseResult 对象，直接返回它
            if (result instanceof ResponseResult) {
                return (ResponseResult<T>) result;
            }

            // 否则，返回成功响应
            return ResponseResult.success("操作成功", result);
        } catch (Exception e) {
            // 捕获业务逻辑异常，返回标准化错误响应
            return ResponseResult.InternalServerError("服务器内部错误，请稍后重试！");
        } finally {
            // 确保锁释放
            releaseLock(lockKey, lockValue);
        }
    }

    /**
     * 检查给定的 member 是否是 key 对应集合中的成员。
     *
     * @param key     集合对应的 key
     * @param member  要检查的成员
     * @return 如果是成员返回 true，否则返回 false；如果 key 不存在也返回 false
     */
    public boolean sIsMember(String key, String member) {
        // 首先检查key是否存在
        if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
            return false;
        }

        SetOperations<String, String> setOps = redisTemplate.opsForSet();
        return Boolean.TRUE.equals(setOps.isMember(key, member));
    }

    /**
     * 向 key 对应的集合中添加一个成员。
     *
     * @param key     集合对应的 key
     * @param members 要添加的一个或多个成员
     */
    public void sAdd(String key, String members) {
        SetOperations<String, String> setOps = redisTemplate.opsForSet();
        setOps.add(key, members);
    }

    /**
     * 移除 key 对应的集合中的一个成员。
     *
     * @param key     集合对应的 key
     * @param members 要移除的一个或多个成员
     */
    public void sRem(String key, String members) {
        SetOperations<String, String> setOps = redisTemplate.opsForSet();
        setOps.remove(key, members);
    }

    /**
     * 获取给定 key 对应集合中的成员数量。
     *
     * @param key 集合对应的 key
     * @return 成员数量；如果 key 不存在则返回 0
     */
    public long getLikeCount(String key) {
        SetOperations<String, String> setOps = redisTemplate.opsForSet();
        return setOps.size(key) != null ? setOps.size(key) : 0;
    }


    /**
     * 批量获取给定 keys 对应集合中的成员数量。
     *
     * @param keys 集合对应的 key 列表
     * @return 包含每个 key 及其对应成员数量的映射
     */
    public Map<String, Long> getLikeCounts(List<String> keys) {
        SetOperations<String, String> setOps = redisTemplate.opsForSet();
        Map<String, Long> likeCounts = new HashMap<>();

        for (String key : keys) {
            Long count = setOps.size(key);
            likeCounts.put(key, count != null ? count : 0L);
        }

        return likeCounts;
    }


    /**
     * 增加或减少有序集合中某个成员的点赞量。
     *
     * @param key   有序集合对应的 key
     * @param member 成员（member）
     * @param delta 点赞量变化量（可以是正数也可以是负数）
     * @return 操作后的新点赞量；如果操作失败则抛出 RuntimeException
     */
    public Long incrementZSetLikeCount(String key, String member, long delta) {
        try {
            Double score = redisTemplate.opsForZSet().incrementScore(key, member, delta);
            return (score != null) ? score.longValue() : null;
        } catch (Exception e) {
            throw new RuntimeException("Redis ZSet like count increment failed", e);
        }
    }

    /**
     * 获取有序集合中某个成员的点赞量。
     *
     * @param key    有序集合对应的 key
     * @param member 成员（member）
     * @return 成员的点赞量；如果成员不存在则返回 null
     */
    public Long getZSetLikeCount(String key, String member) {
        try {
            Double score = redisTemplate.opsForZSet().score(key, member);
            return (score != null) ? score.longValue() : null;
        } catch (Exception e) {
            throw new RuntimeException("Redis ZSet like count retrieval failed", e);
        }
    }


    /**
     * 获取有序集合中前 N 名成员及其对应的点赞量。
     *
     * @param key   有序集合对应的 key
     * @param topN  需要获取的排行条目数量
     * @return 包含排名信息的列表；如果获取失败则抛出 RuntimeException
     */
    public List<RankingItem> getTopNFromZSet(String key, int topN) {
        try {
            // 使用 ZREVRANGE 获取前 N 名的评论及其点赞数
            Set<ZSetOperations.TypedTuple<String>> ranking = redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, topN - 1);

            // 将排行榜转换为适当的格式返回给前端
            List<RankingItem> rankingItems = new ArrayList<>();
            if (ranking != null) {
                for (ZSetOperations.TypedTuple<String> tuple : ranking) {
                    RankingItem item = new RankingItem();
                    item.setCommentId(Integer.valueOf(Objects.requireNonNull(tuple.getValue())));
                    item.setLikeCount((long) Math.floor(tuple.getScore())); // 确保分数为整数
                    rankingItems.add(item);
                }
            }
            System.out.println(rankingItems);
            return rankingItems;
        } catch (Exception e) {
            throw new RuntimeException("获取排行榜失败：" + e.getMessage(), e);
        }
    }
    /**
     * 从 Redis 中获取缓存的排行榜数据，并反序列化为 List<Map<String, Object>>。
     *
     * @param key Redis 缓存键名
     * @return 包含排行榜信息的列表；如果找不到则返回 null
     */
    public List<Map<String, Object>> getRankingListFromCache(String key) {
        try {
            String jsonValue = redisTemplate.opsForValue().get(key);
            if (jsonValue == null || jsonValue.isEmpty()) {
                return null;
            }
            // 反序列化 JSON 字符串为 List<Map<String, Object>>
            return objectMapper.readValue(jsonValue, new TypeReference<>() {
            });
        } catch (Exception e) {
            throw new RuntimeException("获取 Redis 排行榜缓存失败：" + e.getMessage(), e);
        }
    }

    /**
     * 将排行榜数据序列化为 JSON 字符串并缓存到 Redis 中，并设置过期时间。
     *
     * @param key     Redis 缓存键名
     * @param ranking 排行榜数据
     * @param ttl     缓存过期时间（单位：秒、分钟、小时、天等）
     * @param unit    时间单位
     */
    public void cacheRankingList(String key, List<Map<String, Object>> ranking, long ttl, TimeUnit unit) {
        try {
            // 序列化排行榜数据为 JSON 字符串
            String jsonValue = objectMapper.writeValueAsString(ranking);
            redisTemplate.opsForValue().set(key, jsonValue);
            redisTemplate.expire(key, ttl, unit);
        } catch (Exception e) {
            throw new RuntimeException("缓存 Redis 排行榜数据失败：" + e.getMessage(), e);
        }
    }
    /**
     * 分布式锁业务逻辑接口
     */
    @FunctionalInterface
    public interface LockAction<T> {
        T execute();
    }
}
